using System;
using System.Collections.Generic;

namespace Fadd.Commands
{
    /// <summary>
    /// Invokes all handlers for a command.
    /// </summary>
    internal class AsyncQueueItem
    {
        private readonly object _source;
        private readonly Command _command;
        private readonly Queue<CommandHandler> _handlers = new Queue<CommandHandler>();
        private readonly CommandManager _cmdMgr;
        private CommandHandler _current;
        private readonly AsyncQueueItemResult _result;
        private readonly AsyncCallback _callback;

        /// <summary>
        /// Initializes a new instance of the <see cref="AsyncQueueItem"/> class.
        /// </summary>
        /// <param name="source">used as "source" in the command event delegate.</param>
        /// <param name="cmd">command being executed.</param>
        /// <param name="cmdMgr">Used to check if propagation may be cancelled.</param>
        /// <param name="callBack">callback when command have been executed..</param>
        /// <param name="result">async result.</param>
        public AsyncQueueItem(object source, Command cmd, CommandManager cmdMgr, AsyncCallback callBack, AsyncQueueItemResult result)
        {
            _source = source;
            _command = cmd;
            _cmdMgr = cmdMgr;
            _result = result;
            _callback = callBack;
        }

        public void Add(CommandHandler handler)
        {
            _handlers.Enqueue(handler);
        }

        public bool BeginInvoke()
        {
            if (_handlers.Count == 0)
                return false;

            _current = _handlers.Dequeue();
            _current.BeginInvoke(_source, _result.Args, OnEndInvoke, null);
            return true;
        }

        private void OnEndInvoke(IAsyncResult ar)
        {
            if (_current.EndInvoke(ar))
                _command.SetHandled(true);

            if (_result.Args.CancelPropagation
                && _cmdMgr.InvokePropagationCancelled(_command, _current))
            {
                TriggerCompleted();
                return;
            }

            // Completed if list is empty
            if (!BeginInvoke())
                TriggerCompleted();
        }

        private void TriggerCompleted()
        {
            // Check if the unhandled event handles the command
            if (!_command.IsHandled)
                _cmdMgr.TriggerUnhandled(_command);

            _result.ToggleCompleted();
            if (_callback != null)
                _callback(_result);
                
        }
    }
}
